/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "Wed_framework.h"
#include <open62541/server.h>
#include <open62541/plugin/log_stdout.h>

/*******************************************************************************
 * Static
 ******************************************************************************/
static struct Wed_thread *__g_p_pub_thread = NULL;

/*******************************************************************************
 * Macro operate
 ******************************************************************************/
/* \brief 以太网传输配置文件 */
#define ETH_TRANSPORT_PROFILE    "http://opcfoundation.org/UA-Profile/Transport/pubsub-eth-uadp"
#define PUBLISHER_ID_SUB          2234
#define WRITER_GROUP_ID_SUB       101
#define DATA_SET_WRITER_ID_SUB    62541
#define REPEATED_NODECOUNTS       2

/*******************************************************************************
 * Static
 ******************************************************************************/
static UA_Boolean              runningServer                               = UA_TRUE;
static UA_NodeId               runningSubStatusNodeID;
static UA_NodeId               subRepeatedCountNodeID;
static UA_NodeId               connectionIdentSubscriber;
static UA_NodeId               readerGroupIdentifier;
static UA_NodeId               readerIdentifier;
static UA_DataSetReaderConfig  readerConfig;
static UA_Boolean             *runningSub                                  = NULL;
static UA_DataValue           *runningSubDataValueRT                       = NULL;
static UA_UInt64              *subRepeatedCounterData[REPEATED_NODECOUNTS] = {NULL};
static UA_DataValue           *subRepeatedDataValueRT[REPEATED_NODECOUNTS] = {NULL};
static UA_NodeId               subNodeID;
static UA_UInt64              *subCounterData                              = NULL;
static UA_DataValue           *subDataValueRT                              = NULL;

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief 添加服务器节点
 */
static int addServerNodes(UA_Server *p_server) {
    int                   ret;
    UA_NodeId             objectId;
    UA_NodeId             newNodeId;
    UA_VariableAttributes runningStatusSubscriber = UA_VariableAttributes_default;
    UA_Boolean            runningSubStatusValue   = 0;
    UA_VariableAttributes subscriberAttr          = UA_VariableAttributes_default;
    UA_UInt64 subscribeValue                      = 0;
    UA_ObjectAttributes object                    = UA_ObjectAttributes_default;
    object.displayName                            = UA_LOCALIZEDTEXT("en-US", "Counter Object");

    /* 添加一个对象节点 */
    ret = UA_Server_addObjectNode(p_server, UA_NODEID_NULL,
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                  UA_QUALIFIEDNAME(1, "Counter Object"), UA_NODEID_NULL,
                                  object, NULL, &objectId);
    if (ret != UA_STATUSCODE_GOOD) {
        return ret;
    }

    subscriberAttr.accessLevel           = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    subscriberAttr.dataType              = UA_TYPES[UA_TYPES_UINT64].typeId;
    UA_Variant_setScalar(&subscriberAttr.value, &subscribeValue, &UA_TYPES[UA_TYPES_UINT64]);
    subscriberAttr.displayName           = UA_LOCALIZEDTEXT("en-US", "Subscriber Counter");
    newNodeId                            = UA_NODEID_STRING(1, "SubscriberCounter");
    ret = UA_Server_addVariableNode(p_server, newNodeId, objectId,
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                    UA_QUALIFIEDNAME(1, "Subscriber Counter"),
                                    UA_NODEID_NULL, subscriberAttr, NULL, &subNodeID);
    if (ret != UA_STATUSCODE_GOOD) {
        return ret;
    }

    for (UA_Int32 iterator = 0; iterator < REPEATED_NODECOUNTS; iterator++) {
        UA_VariableAttributes repeatedNodeSub = UA_VariableAttributes_default;
        UA_DateTime           repeatedSubscribeValue;

        UA_Variant_setScalar(&repeatedNodeSub.value, &repeatedSubscribeValue, &UA_TYPES[UA_TYPES_UINT64]);
        repeatedNodeSub.accessLevel           = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
        repeatedNodeSub.dataType              = UA_TYPES[UA_TYPES_UINT64].typeId;
        repeatedNodeSub.displayName           = UA_LOCALIZEDTEXT("en-US", "Subscriber RepeatedCounter");
        newNodeId                             = UA_NODEID_NUMERIC(1, (UA_UInt32)iterator+50000);
        ret = UA_Server_addVariableNode(p_server, newNodeId, objectId,
                                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                        UA_QUALIFIEDNAME(1, "Subscriber RepeatedCounter"),
                                        UA_NODEID_NULL, repeatedNodeSub, NULL, &subRepeatedCountNodeID);
        if (ret != UA_STATUSCODE_GOOD) {
            return ret;
        }
    }

    /* 初始化变量属性 */
    runningStatusSubscriber.accessLevel           = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    UA_Variant_setScalar(&runningStatusSubscriber.value, &runningSubStatusValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
    runningStatusSubscriber.displayName           = UA_LOCALIZEDTEXT("en-US", "RunningStatus Sub");
    runningStatusSubscriber.dataType              = UA_TYPES[UA_TYPES_BOOLEAN].typeId;
    newNodeId                                     = UA_NODEID_NUMERIC(1, (UA_UInt32)30000);
    /* 添加服务器变量节点 */
    return UA_Server_addVariableNode(p_server, newNodeId, objectId,
                                     UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                     UA_QUALIFIEDNAME(1, "RunningStatus Sub"),
                                     UA_NODEID_NULL, runningStatusSubscriber, NULL, &runningSubStatusNodeID);
}

/**
 * \brief 添加 PubSub 订阅者连接
 */
static int addPubSubConnectionSubscriber(UA_Server *server, UA_String *transportProfile,
                                          UA_NetworkAddressUrlDataType *networkAddressUrlSubscriber){
    UA_StatusCode retval = UA_STATUSCODE_GOOD;

    UA_PubSubConnectionConfig connectionConfig;

    memset(&connectionConfig, 0, sizeof(connectionConfig));
    connectionConfig.name                                   = UA_STRING("Subscriber Connection");
    connectionConfig.enabled                                = UA_TRUE;
    UA_NetworkAddressUrlDataType networkAddressUrlsubscribe = *networkAddressUrlSubscriber;
    connectionConfig.transportProfileUri                    = *transportProfile;

    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrlsubscribe, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);

    connectionConfig.publisherIdType                        = UA_PUBLISHERIDTYPE_UINT32;
    /* 生成一个 32 位随机数 */
    connectionConfig.publisherId.uint32                     = UA_UInt32_random();

    /* 添加 PubSub 连接 */
    retval |= UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdentSubscriber);
    if (retval == UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,"The PubSub Connection was created successfully!");
    }
    return retval;
}

/**
 * \brief 添加 ReaderGroup 到已创建的连接中
 */
static int addReaderGroup(UA_Server *p_server) {
    UA_ReaderGroupConfig readerGroupConfig;

    memset (&readerGroupConfig, 0, sizeof(UA_ReaderGroupConfig));
    readerGroupConfig.name    = UA_STRING("ReaderGroup");
    readerGroupConfig.rtLevel = UA_PUBSUB_RT_DETERMINISTIC;

    return UA_Server_addReaderGroup(p_server, connectionIdentSubscriber, &readerGroupConfig,
                                    &readerGroupIdentifier);
}

static UA_StatusCode externalDataWriteCallback(UA_Server *server, const UA_NodeId *sessionId,
                                               void *sessionContext, const UA_NodeId *nodeId,
                                               void *nodeContext, const UA_NumericRange *range,
                                               const UA_DataValue *data){
    //node values are updated by using variables in the memory
    //UA_Server_write is not used for updating node values.
    return UA_STATUSCODE_GOOD;
}

static UA_StatusCode externalDataReadNotificationCallback(UA_Server *server, const UA_NodeId *sessionId,
                                                          void *sessionContext, const UA_NodeId *nodeid,
                                                          void *nodeContext, const UA_NumericRange *range){
    //allow read without any preparation
    return UA_STATUSCODE_GOOD;
}

/* Set SubscribedDataSet type to TargetVariables data type
 * Add subscribedvariables to the DataSetReader */
static int addSubscribedVariables (UA_Server *p_server) {
    UA_Int32        iterator              = 0;
    UA_Int32        iteratorRepeatedCount = 0;
    UA_ValueBackend runningSubvalueBackend;

    UA_FieldTargetVariable *targetVars = (UA_FieldTargetVariable *)UA_calloc((REPEATED_NODECOUNTS + 2), sizeof(UA_FieldTargetVariable));
    if (!targetVars) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "FieldTargetVariable - Bad out of memory");
        return -WED_ENOMEM;
    }

    /* 分配一个布尔类型数据 */
    runningSub = UA_Boolean_new();
    if (!runningSub) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "runningsub - Bad out of memory");
        UA_free(targetVars);
        return -WED_ENOMEM;;
    }

    /* 分配一个数据值结构体 */
    *runningSub = UA_TRUE;
    runningSubDataValueRT = UA_DataValue_new();
    if (!runningSubDataValueRT) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "runningsubDatavalue - Bad out of memory");
        UA_free(targetVars);
        return -WED_ENOMEM;;
    }
    /* 设置数据值结构体为标量数据 */
    UA_Variant_setScalar(&runningSubDataValueRT->value, runningSub, &UA_TYPES[UA_TYPES_BOOLEAN]);
    runningSubDataValueRT->hasValue = UA_TRUE;

    /* 将上述创建节点的值后端设置为 “外部值源” */
    runningSubvalueBackend.backendType = UA_VALUEBACKENDTYPE_EXTERNAL;          /* 引用外部的内存 */
    runningSubvalueBackend.backend.external.value = &runningSubDataValueRT;
    runningSubvalueBackend.backend.external.callback.userWrite = externalDataWriteCallback;
    runningSubvalueBackend.backend.external.callback.notificationRead = externalDataReadNotificationCallback;
    UA_Server_setVariableNode_valueBackend(p_server, UA_NODEID_NUMERIC(1, (UA_UInt32)30000), runningSubvalueBackend);

    UA_FieldTargetDataType_init(&targetVars[iterator].targetVariable);
    targetVars[iterator].targetVariable.attributeId  = UA_ATTRIBUTEID_VALUE;
    targetVars[iterator].targetVariable.targetNodeId = UA_NODEID_NUMERIC(1, (UA_UInt32)30000);
    iterator++;

    /* 创建目标变量 */
    for (iterator = 1, iteratorRepeatedCount = 0; iterator <= REPEATED_NODECOUNTS; iterator++, iteratorRepeatedCount++) {
        subRepeatedCounterData[iteratorRepeatedCount] = UA_UInt64_new();
        if (!subRepeatedCounterData[iteratorRepeatedCount]) {
            UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "SubscribeRepeatedCounterData - Bad out of memory");
            UA_free(targetVars);
            return -WED_ENOMEM;
        }

        *subRepeatedCounterData[iteratorRepeatedCount] = 0;
        subRepeatedDataValueRT[iteratorRepeatedCount] = UA_DataValue_new();
        if (!subRepeatedDataValueRT[iteratorRepeatedCount]) {
            UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "SubscribeRepeatedCounterDataValue - Bad out of memory");
            UA_free(targetVars);
            return -WED_ENOMEM;
        }

        UA_Variant_setScalar(&subRepeatedDataValueRT[iteratorRepeatedCount]->value, subRepeatedCounterData[iteratorRepeatedCount], &UA_TYPES[UA_TYPES_UINT64]);
        subRepeatedDataValueRT[iteratorRepeatedCount]->hasValue = UA_TRUE;

        /* 将上述创建节点的值后端设置为 “外部值源” */
        UA_ValueBackend valueBackend;
        valueBackend.backendType = UA_VALUEBACKENDTYPE_EXTERNAL;
        valueBackend.backend.external.value = &subRepeatedDataValueRT[iteratorRepeatedCount];
        valueBackend.backend.external.callback.userWrite = externalDataWriteCallback;
        valueBackend.backend.external.callback.notificationRead = externalDataReadNotificationCallback;
        UA_Server_setVariableNode_valueBackend(p_server, UA_NODEID_NUMERIC(1, (UA_UInt32)iteratorRepeatedCount + 50000), valueBackend);

        UA_FieldTargetDataType_init(&targetVars[iterator].targetVariable);
        targetVars[iterator].targetVariable.attributeId  = UA_ATTRIBUTEID_VALUE;
        targetVars[iterator].targetVariable.targetNodeId = UA_NODEID_NUMERIC(1, (UA_UInt32)iteratorRepeatedCount + 50000);
    }

    subCounterData = UA_UInt64_new();
    if (!subCounterData) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "SubscribeCounterData - Bad out of memory");
        UA_free(targetVars);
        return -WED_ENOMEM;
    }

    *subCounterData = 0;
    subDataValueRT = UA_DataValue_new();
    if (!subDataValueRT) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "SubscribeDataValue - Bad out of memory");
        UA_free(targetVars);
        return -WED_ENOMEM;
    }

    UA_Variant_setScalar(&subDataValueRT->value, subCounterData, &UA_TYPES[UA_TYPES_UINT64]);
    subDataValueRT->hasValue = UA_TRUE;

    /* 将上述创建节点的值后端设置为 “外部值源” */
    UA_ValueBackend valueBackend;
    valueBackend.backendType = UA_VALUEBACKENDTYPE_EXTERNAL;
    valueBackend.backend.external.value = &subDataValueRT;
    valueBackend.backend.external.callback.userWrite = externalDataWriteCallback;
    valueBackend.backend.external.callback.notificationRead = externalDataReadNotificationCallback;
    UA_Server_setVariableNode_valueBackend(p_server, subNodeID, valueBackend);

    UA_FieldTargetDataType_init(&targetVars[iterator].targetVariable);
    targetVars[iterator].targetVariable.attributeId  = UA_ATTRIBUTEID_VALUE;
    targetVars[iterator].targetVariable.targetNodeId = subNodeID;

    /* 设置订阅数据的目标变量 */
    readerConfig.subscribedDataSetType = UA_PUBSUB_SDS_TARGET;
    readerConfig.subscribedDataSet.subscribedDataSetTarget.targetVariables = targetVars;
    readerConfig.subscribedDataSet.subscribedDataSetTarget.targetVariablesSize = REPEATED_NODECOUNTS + 2;

    return 0;
}

/**
 * \brief 添加 DataSetReader 到 ReaderGroup 中
 */
static int addDataSetReader(UA_Server *p_server) {
    int                                  ret;
    UA_Int32                             iterator             = 0;
    UA_UInt16                            publisherIdentifier  = PUBLISHER_ID_SUB;
    UA_UadpDataSetReaderMessageDataType *dataSetReaderMessage = NULL;
    UA_DataSetMetaDataType              *pMetaData            = NULL;

    memset (&readerConfig, 0, sizeof(UA_DataSetReaderConfig));
    readerConfig.name                 = UA_STRING("DataSet Reader");    /* 数据集订阅信息名称 */
    readerConfig.publisherId.type     = &UA_TYPES[UA_TYPES_UINT16];     /* 发布者 ID 类型 */
    readerConfig.publisherId.data     = &publisherIdentifier;           /* 发布者 ID  */
    readerConfig.writerGroupId        = WRITER_GROUP_ID_SUB;            /* 发布者 writerGroup ID */
    readerConfig.dataSetWriterId      = DATA_SET_WRITER_ID_SUB;         /* 发布者 writerGroup 数据集 ID */

    readerConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED; /* 无编码数据 */
    readerConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_UADPDATASETREADERMESSAGEDATATYPE];  /* 数据类型为 OPC UA 定义的二进制网络消息编码格式 */

    /* 分配一个新的 UADP DataSetReader 消息数据类型结构体 */
    dataSetReaderMessage = UA_UadpDataSetReaderMessageDataType_new();
    /* 设置 UADP DataSetReader 消息头部信息所包含的信息
     * 包含发布者 ID，GroupHeader，WriterGroup ID 和 PayloadHeader */
    dataSetReaderMessage->networkMessageContentMask = (UA_UadpNetworkMessageContentMask)(UA_UADPNETWORKMESSAGECONTENTMASK_PUBLISHERID |
                                                      (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPHEADER |
                                                      (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_WRITERGROUPID |
                                                      (UA_UadpNetworkMessageContentMask)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER);
    readerConfig.messageSettings.content.decoded.data = dataSetReaderMessage;

    /* 初始化元数据配置 */
    pMetaData = &readerConfig.dataSetMetaData;
    UA_DataSetMetaDataType_init(pMetaData);

    /* 初始化字段元数据 */
    pMetaData->fieldsSize = REPEATED_NODECOUNTS + 2;
    pMetaData->fields     = (UA_FieldMetaData*)UA_Array_new(pMetaData->fieldsSize, &UA_TYPES[UA_TYPES_FIELDMETADATA]);
    /* 第一个字段数据类型为布尔类型 */
    UA_FieldMetaData_init(&pMetaData->fields[iterator]);
    UA_NodeId_copy(&UA_TYPES[UA_TYPES_BOOLEAN].typeId,
                   &pMetaData->fields[iterator].dataType);
    pMetaData->fields[iterator].builtInType = UA_NS0ID_BOOLEAN;
    pMetaData->fields[iterator].valueRank   = -1;                /* 标量 */
    iterator++;

    for (iterator = 1; iterator <= REPEATED_NODECOUNTS; iterator++) {
        UA_FieldMetaData_init(&pMetaData->fields[iterator]);
        /* 无符号 64 位数据类型 */
        UA_NodeId_copy(&UA_TYPES[UA_TYPES_UINT64].typeId,
                       &pMetaData->fields[iterator].dataType);
        pMetaData->fields[iterator].builtInType = UA_NS0ID_UINT64;
        pMetaData->fields[iterator].valueRank   = -1;              /* 标量 */
    }

    /* Unsigned Integer DataType */
    UA_FieldMetaData_init(&pMetaData->fields[iterator]);
    UA_NodeId_copy (&UA_TYPES[UA_TYPES_UINT64].typeId,
                    &pMetaData->fields[iterator].dataType);
    pMetaData->fields[iterator].builtInType = UA_NS0ID_UINT64;
    pMetaData->fields[iterator].valueRank   = -1;              /* 标量 */

    /* 设置订阅信息的目标变量 */
    ret = addSubscribedVariables(p_server);
    if (ret != UA_STATUSCODE_GOOD) {
        return ret;
    }

    /* 初始化 DataSetReader 的元数据配置 */
    ret = UA_Server_addDataSetReader(p_server, readerGroupIdentifier, &readerConfig,
                                     &readerIdentifier);
    UA_free(readerConfig.subscribedDataSet.subscribedDataSetTarget.targetVariables);
    UA_free(readerConfig.dataSetMetaData.fields);
    UA_UadpDataSetReaderMessageDataType_delete(dataSetReaderMessage);

    return ret;
}

/**
 * \brief 应用任务函数
 */
static void __opcua_pub_task(void *p_arg){
    int                          ret;
    UA_String                    transportProfile;
    UA_NetworkAddressUrlDataType networkAddressUrlSub;
    //UA_NetworkAddressUrlDataType networkAddressUrlPub;

    UA_Server *p_server = UA_Server_new();
    if (p_server == NULL) {
        WED_ERR_PRINT("OPC UA server create failed");
        goto __poll;
    }
    WED_INFO_PRINT("OPC UA server create successful");

    networkAddressUrlSub.networkInterface = UA_STRING("/dev/eth_tsn");
    networkAddressUrlSub.url              = UA_STRING("opc.eth://00-11-22-33-44-55");
    //networkAddressUrlPub.networkInterface = UA_STRING("/dev/eth_tsn");
    //networkAddressUrlPub.url              = UA_STRING("opc.eth://pub");

    transportProfile = UA_STRING(ETH_TRANSPORT_PROFILE);

    ret = addServerNodes(p_server);
    if (ret != UA_STATUSCODE_GOOD) {
        WED_ERR_PRINT("OPC UA server nodes add failed(%d)", ret);
        goto __poll;
    }
    WED_INFO_PRINT("OPC UA server nodes add successful");

  //  addPubSubConnection(p_server, &transportProfile, &networkAddressUrlPub);
    /* 添加 PubSub 订阅连接 */
    ret = addPubSubConnectionSubscriber(p_server, &transportProfile, &networkAddressUrlSub);
    if (ret != UA_STATUSCODE_GOOD) {
        WED_ERR_PRINT("OPC UA server PubSub connection subscriber add failed(%d)", ret);
        goto __poll;
    }
    WED_INFO_PRINT("OPC UA server PubSub connection subscriber add successful");

    /* 添加 readerGroups */
    ret = addReaderGroup(p_server);
    if (ret != UA_STATUSCODE_GOOD) {
        WED_ERR_PRINT("OPC UA server ReaderGroup add failed(%d)", ret);
        goto __poll;
    }
    WED_INFO_PRINT("OPC UA server ReaderGroup add successful");

    /* 添加 DataSetReader 到 ReaderGroup 中 */
    ret = addDataSetReader(p_server);
    if (ret != UA_STATUSCODE_GOOD) {
        WED_ERR_PRINT("OPC UA server DataSetReader add failed(%d)", ret);
        goto __poll;
    }
    WED_INFO_PRINT("OPC UA server DataSetReader add successful");

    /* 冻结 ReaderGroup 配置 */
    UA_Server_freezeReaderGroupConfiguration(p_server, readerGroupIdentifier);
    /* 设置 ReaderGroup 为正在操作中状态 */
    UA_Server_setReaderGroupOperational(p_server, readerGroupIdentifier);
    /* 服务器运行 */
    ret = UA_Server_run(p_server, &runningServer);

    WED_ERR_PRINT("OPC UA server run failed(%d)", ret);
__poll:
    while(1) {
        //Wed_kprintf("app %d main1 %d\r\n", a, i++);
        Wed_mdelay(1000);
    }
}

/**
 * \brief OPC UA PUBSUB TSN 主函数
 *
 * @return 成功返回 0
 */
int opcua_pubsub_tsn_main(void){
    /* 创建线程 */
    __g_p_pub_thread = Wed_thread_create(__opcua_pub_task,
                                           NULL,
                                          "opcua_pub",
                                           10240,
                                           0);
    if (__g_p_pub_thread == NULL) {
        WED_ERR_PRINT("OPCUA pub thread create failed");
        return -WED_ECREAT;
    }
    return 0;
}


